package com.zhuxin.common;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient;
import org.apache.http.impl.nio.client.HttpAsyncClients;
import org.apache.http.impl.nio.conn.PoolingNHttpClientConnectionManager;
import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor;
import org.apache.http.nio.reactor.ConnectingIOReactor;
import org.apache.http.nio.reactor.IOReactorException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.ResponseEntity;
import org.springframework.http.client.HttpComponentsAsyncClientHttpRequestFactory;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.util.concurrent.ListenableFuture;
import org.springframework.util.concurrent.ListenableFutureCallback;
import org.springframework.web.client.AsyncRestTemplate;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.Map;
import java.util.concurrent.Semaphore;

/**
 * Created by lzb on 2017/12/18.
 */
public class ReqObject {
    private static Logger log = LoggerFactory.getLogger(ReqObject.class);
    private static Semaphore semaphore = new Semaphore(3000, true);
    String url;
    HttpHeaders heads = new HttpHeaders();
    MultiValueMap<String, Object> params = new LinkedMultiValueMap<String, Object>();
    private static AsyncRestTemplate asyncRestTemplate;
    private static RestTemplate restTemplate;
    public ReqObject(String url) {



        if(restTemplate==null){
            synchronized (ReqObject.class) {
                if(restTemplate==null){
                    PoolingHttpClientConnectionManager pool=new PoolingHttpClientConnectionManager();
                    pool.setMaxTotal(1000);
                    pool.setDefaultMaxPerRoute(1000);

                    CloseableHttpClient client= HttpClients.custom().setConnectionManager(pool).build();
                    HttpComponentsClientHttpRequestFactory factory=new HttpComponentsClientHttpRequestFactory(client);
                    factory.setReadTimeout(Integer.MAX_VALUE);//等待最大值.
                    factory.setConnectTimeout(300000);//连接超时
                    factory.setConnectionRequestTimeout(Integer.MAX_VALUE);//连接池超时。。。

                    restTemplate=new RestTemplate(factory);
//                    restTemplate.getInterceptors().add(new CookieHttpRequestInterceptor());
                }
            }
        }

        if (asyncRestTemplate == null) {
            synchronized (ReqObject.class) {
                if (asyncRestTemplate == null) {
                    ConnectingIOReactor ioReactor = null;
                    try {
                        ioReactor = new DefaultConnectingIOReactor();
                    } catch (IOReactorException e) {
                        e.printStackTrace();
                    }
                    PoolingNHttpClientConnectionManager pool = new PoolingNHttpClientConnectionManager(ioReactor);
                    pool.setMaxTotal(1000);
                    pool.setDefaultMaxPerRoute(1000);
                    //        HttpHost localhost = new HttpHost("10.1.10.164", 5555);
                    //        pool.setMaxPerRoute(new HttpRoute(localhost), 3);

                    CloseableHttpAsyncClient httpAsyncClient = HttpAsyncClients.custom().setConnectionManager(pool).build();
                    httpAsyncClient.start();
                    HttpComponentsAsyncClientHttpRequestFactory httpRequestFactory = new HttpComponentsAsyncClientHttpRequestFactory(httpAsyncClient);
                    httpRequestFactory.setReadTimeout(Integer.MAX_VALUE);//等待最大值.
                    httpRequestFactory.setConnectTimeout(300000);//连接超时
                    httpRequestFactory.setConnectionRequestTimeout(Integer.MAX_VALUE);//连接池超时。。。

                    //        httpRequestFactory.
                    //        httpRequestFactory.afterPropertiesSet();.
                    //        httpRequestFactory.getHttpClient().getParams().setIntParameter()
                    asyncRestTemplate = new AsyncRestTemplate(httpRequestFactory);
                }
            }
        }
        this.url = url;
    }

    public ReqObject addParam(String key, Object value) {
        params.add(key, value);
        return this;
    }

    public ReqObject addHead(String key, String value) {
        heads.add(key, value);
        return this;
    }


    public <T> void syncdeal(ParameterizedTypeReference<T> glzz, ListenableFutureCallback<ResponseEntity<T>> callbackFunc) {
//        try {
//            semaphore.acquire();
            HttpEntity<MultiValueMap> request = new HttpEntity<MultiValueMap>(params, heads);
            ListenableFuture<ResponseEntity<T>> future = asyncRestTemplate.exchange(url, HttpMethod.POST, request, glzz);
            future.addCallback(callbackFunc);
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        } finally {
//            semaphore.release();
//        }
    }

    public <T> T deal(Class<T> glzz) {
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        HttpEntity<MultiValueMap> request = new HttpEntity<MultiValueMap>(params, heads);
        try {
            return restTemplate.postForObject(url, request, glzz);
        } catch (Exception e) {
            for (Map.Entry<String, List<Object>> entry : params.entrySet()) {
                log.error("远程调用出错!URL:{},key:{},values:{}", this.url, entry.getKey(), ArrayUtils.toString(entry.getValue()));
            }
            throw e;
        } finally {
            semaphore.release();
        }
    }

    public <T> T get(Class<T> glzz) {
        try {
            semaphore.acquire();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        HttpEntity<MultiValueMap> request = new HttpEntity<MultiValueMap>(params, heads);
        try {
            return restTemplate.getForObject(url, glzz);
        } catch (Exception e) {
            for (Map.Entry<String, List<Object>> entry : params.entrySet()) {
                log.error("远程调用出错!URL:{},key:{},values:{}", this.url, entry.getKey(), ArrayUtils.toString(entry.getValue()));
            }
            throw e;
        } finally {
            semaphore.release();
        }
    }

    public <T> ResponseEntity<T> exchange(ParameterizedTypeReference<T> typeRef) {
//        ParameterizedTypeReference<T> typeRef = new ParameterizedTypeReference<T>() {};
        HttpEntity<MultiValueMap> request = new HttpEntity<MultiValueMap>(params, heads);
        return restTemplate.exchange(
                url,
                HttpMethod.POST,
                request,
                typeRef);
    }
}